<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Js实现继承的方式</title>
</head>
<body>
  <script>
    /**************************************************************/
    console.log('/*******************************1、原型继承*******************************/')
    // 1、原型继承
    // 把子类的原型指向要继承的父类
    //父类
    // function ParentClass(name) {
    //     this.name = name
    // }
    // ParentClass.prototype.eatName = 'xx';
    // ParentClass.prototype.eat = function (name) {
    //     console.log('这是一个' + name);
    // };
    //子类
    // function ChildClass(age) {
    //     this.age = age
    // }
    /* 将 ChildClass 的原型直接赋值为 ParentClass的构造函数，
      所以此时ChildClass的原型对象中的 constructor其实是指向ParentClass的构造函数的，
      所以我们这边要修改回来，不然引起原型链的絮乱 */
    // ChildClass.prototype = new ParentClass();
    // ChildClass.prototype.constuctor = ChildClass;
    // let obj = new ChildClass('12', 22);
    // obj.eat('西红柿');
    // console.log(obj.eatName); // xx
    // 缺点：只能继承父类原型上的方法和属性，不能继承父类的实例属性和方法，多个实例对引用类型的操作会被篡改。

    /**************************************************************/
    console.log('/*******************************2、构造函数继承*******************************/')
    // 2、构造函数继承
    // 使用parentClass.call(this)改变this的指向来调用父类的属性和方法
    //父类
    // function ParentClass(name) {
    //   this.name = name;
    //   this.eat = function(){
    //       console.log('这是一个' + name);
    //   }
    // }

    // //子类
    // function ChildClass(name,age) {
    //     ParentClass.call(this,name)
    //     this.age = age
    // }

    // let obj = new ChildClass('西红柿', 22);
    // console.log(obj.name,obj.age);
    // obj.eat() // 这是一个西红柿
    // 缺点：只能继承父类实例e的属性和方法，不能继承原型上的属性和方法

    /**************************************************************/
    console.log('/*******************************3、组合式继承*******************************/')
    // 3、组合式继承
    // 以上二种继承方式的组合
    //父类
    // function ParentClass(name) {
    //     this.name = name;
    // }
    // ParentClass.prototype.eatName = 'xx';
    // ParentClass.prototype.eat = function () {
    //     console.log('这是一个' + this.name);
    // };
    // //子类
    // function ChildClass(name,age) {
    //     ParentClass.call(this,name)
    //     this.age = age
    // }
    // /* 
    //     将 ChildClass 的原型直接赋值为 ParentClass的构造函数，
    //     所以此时ChildClass的原型对象中的 constructor其实是指向ParentClass的构造函数的，
    //     所以我们这边要修改回来，不然引起原型链的絮乱 
    // */
    // ChildClass.prototype = new ParentClass();
    // ChildClass.prototype.constuctor = ChildClass;
    // let obj = new ChildClass('西红柿', 22);
    // console.log(obj.name,obj.age);
    // obj.eat() // 这是一个西红柿

    console.log('/*******************************4、原型式继承*******************************/')
    // 4、原型式继承
    // 利用Object.creat(parentClass)创建一个原型指向parentClass的对象
    // let parentClass = {
    //     name: '西红柿'
    // };
    // let ChildClass = Object.create(parentClass);
    // ChildClass.age = 11;
    // console.log(ChildClass.name,ChildClass.age); //西红柿 11
    // 缺点：无法传递参数，原型链继承多个实例时，实例引用类型指向相同，存在篡改的可能

    console.log('/*******************************5、寄生式继承*******************************/')
    // 5、寄生式继承
    // 在原型上的基础上新增属性和方法增强函数
    // let parentClass = {
    //     name: '西红柿'
    // };

    // function clone(original){
    //   let obj = Object.create(original);
    //   obj.eat = function(){
    //       console.log('这是一个' + this.name); //西红柿
    //   }
    //   return obj;
    // }

    // let ChildClass = clone(parentClass);
    // console.log(ChildClass.name);
    // ChildClass.eat();
    // 缺点：（同上原型式继承）无法传递参数，原型链继承多个实例时，实例引用类型指向相同，存在篡改的可能

    console.log('/*******************************6、寄生组合式继承*******************************/')
    // 6、寄生组合式继承
    // 将组合式和寄生式结合起来

    //父类
    // function ParentClass(name) {
    //     this.name = name;
    // }
    // ParentClass.prototype.eat = function () {
    //     console.log('这是一个' + this.name);
    // };
    // //子类
    // function ChildClass(name,age) {
    //     ParentClass.call(this,name)
    //     this.age = age
    // }

    // /* 
    //     这里改用 Object.create 就可以减少组合继承中多进行一次构造的过程
    // */
    // ChildClass.prototype = Object.create(ParentClass.prototype);
    // ChildClass.prototype.constuctor = ChildClass;

    // let obj = new ChildClass('西红柿', 22);
    // obj.eat();
    // console.log(obj.age); // 22
    // 目前最成熟的方法

    console.log('/*******************************7、混入方式继承多个对象*******************************/')
    // 7、混入方式继承多个对象

    // 在寄生组合方法中的Object.creat()的下一行使用Object.assin()混合其它的类，

    // Object.assin()会把所有可枚举的属性从一个或多个原对象复制到目标对象。

    //父类
    function ParentClass(name) {
        this.name = name;
    }
    ParentClass.prototype.eat = function () {
        console.log('这是一个' + this.name);
    };

    function ParentClass1(count) {
        this.count = count;
    }
    //子类
    function ChildClass(name,age,count) {
        ParentClass.call(this,name);
        ParentClass1.call(this,count);
        this.age = age
    }

    /* 
        这里改用 Object.create 就可以减少组合继承中多进行一次构造的过程
    */
    ChildClass.prototype = Object.create(ParentClass.prototype);
    //混合其它
    Object.assign(ChildClass.prototype,ParentClass1.prototype)
    ChildClass.prototype.constuctor = ChildClass;

    console.log(ChildClass);
    let obj = new ChildClass('西红柿', 22, 10);
    obj.eat();
    console.log(obj.age,obj.name,obj.count); // 22



    console.log('/*******************************8、ES6类继承*******************************/')
    // 8、ES6类继承

    // 用extends继承父类，在子类的construtor调用super()。

    //父类
    // class ParentClass {
    //     constructor(name){
    //         this.name = name
    //     }
    //     eat(){
    //         console.log('这是一个' + this.name);
    //     }
    // }

    // //子类
    // class ChildClass extends ParentClass{
    //     constructor(name,age){
    //         super(name)
    //         this.age = age
    //     }
    // }

    // let obj = new ChildClass('西红柿', 22);
    // obj.eat();
    // console.log(obj.name,obj.age); // 西红柿 22

    // ES5继承和ES6继承的区别：

    // ES5:先创建子类的实例对象，再把父类的属性和方法添加到this上。

    // ES6：先创建父类的实例对象this，再用子类的构造函数修改this。因为子类没有自己的this，所以必须调用父类的super()方法，否则新建实例会报错
  </script>
</body>
</html>